feat: copilot options API — drop -1m upgrade hack, forward reasoning_effort verbatim#7
Merged
Conversation
The upgradeModel() helper rewrote base Claude model ids (e.g. claude-opus-4.7) to extended variants like -1m-internal / -1m when they appeared in the upstream /models list. The modern Copilot model catalog no longer ships those suffixed ids — base models carry the full context window directly via capabilities.limits — so the rewrite is solving a problem that no longer exists and was hiding the actual model id from callers. Removes the helper, the COPILOT2API_NO_MODEL_UPGRADE env var, the noModelUpgrade flag threaded through NewHandler, the related tests, and the README/CHANGELOG references. BREAKING CHANGE: requests for a base model id are no longer silently rewritten. Pass the variant id explicitly (e.g. claude-opus-4.6-1m on older accounts that still expose it) if you want a different model. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot's modern /models listing declares per-model accepted effort
values in capabilities.supports.reasoning_effort (e.g. xhigh on GPT-5,
minimal on Gemini Flash, max on Anthropic). The previous request
plumbing only knew the legacy thinking_budget numeric and bucketed it
into low|medium|high at 8k/16k thresholds, so modern enum values were
either dropped or flattened.
- Add ReasoningEffort *string to OpenAIChatCompletionsRequest. Accept
any string and forward to upstream; per-model validation is
upstream's job (the supported enum varies per model).
- Both proxy smart-route converters now forward the effort string
directly instead of round-tripping through ThinkingBudget. Legacy
thinking_budget remains as a fallback when no effort is supplied.
- Anthropic→OpenAI bridge (ConvertAnthropicToOpenAI) now honors
output_config.effort with priority over thinking.budget_tokens.
- Stop flattening output_config.effort: "max" → "high" in the
Responses path. max (and xhigh) are first-class effort values per
the current Anthropic SDK and Copilot model capabilities.
Verified end-to-end against the live Copilot upstream: pre-fix
smart-routed bodies for gpt-5.5 with reasoning_effort low/high/xhigh
were identical (field dropped); post-fix the upstream body contains
reasoning: {effort, summary: "detailed"} and completion tokens scale
197→248→276 with effort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
upgradeModel()helper andCOPILOT2API_NO_MODEL_UPGRADEenv var. Live/modelsno longer publishes-1m-internal/-1mids — base models carry the full window viacapabilities.limits.max_context_window_tokens. The silent rewrite was hiding the actual model id from callers.reasoning_effortverbatim end-to-end. The previous code only handled the legacythinking_budgetnumeric and bucketed it lossily (8k/16k →low|medium|high). Modern enum values (xhigh,minimal,none,max) were dropped or flattened on every conversion path.output_config.efforton the Anthropic→OpenAI bridge (previously only the Anthropic→Responses path read it).output_config.effort: "max"to"high"—maxis a first-class value per the current Anthropic SDK.Field/shape matches official OpenAI and Anthropic SDKs (verified via Context7). The per-model accepted enum is upstream's authority — we accept any string and let Copilot reject. Same pattern as
Joouis/agent-maestro.Verification (live upstream)
Identical prompt sent through the smart-route conversion path (
/v1/chat/completions→ upstream/responsesforgpt-5.5):completion_tokenscompletion_tokensreasoningfield){"effort":"low","summary":"detailed"}reasoningfield){"effort":"high","summary":"detailed"}reasoningfield){"effort":"xhigh","summary":"detailed"}Pre-fix token variation is LLM nondeterminism — all three upstream bodies were byte-identical. Post-fix
completion_tokensscales monotonically with effort and the high-effort call took 21s (vs 4-5s for low) — effort takes effect.Direct passthrough cases (
/v1/responsesetc.) continue to be lossless; only the conversion paths needed the plumbing.Breaking changes
COPILOT2API_NO_MODEL_UPGRADEenv var removed. The auto-upgrade it disabled no longer exists.-1m/-1m-internalvariants. Pass the variant id explicitly if you want one (and it still exists on your account).Test plan
go test ./...green; new tests pin (a)reasoning_effortforwarded verbatim through both smart-route converters, (b) effort wins over budget when both supplied, (c)output_config.efforthonored on Anthropic→OpenAI bridge, (d)max/xhighno longer flattened./v1/chat/completionssmart-routed forgpt-5.5shows the upstream body now contains thereasoningblock./v1/responsesdirect passthrough unchanged —reasoning_tokensscales 57→95→135 across low/high/xhigh./v1/messagespaths — no Claude models in this Enterprise plan's/v1/models. Conversion logic covered by unit tests; live verification deferred.Out of scope (follow-ups)
ConvertResponsesResultToChatResponsedoesn't propagateoutput_tokens_details.reasoning_tokens→completion_tokens_details.reasoning_tokens, so the client view of reasoning usage is missing on smart-routed responses.web_search,code_execution, etc.) before forwarding — pattern from agent-maestro to avoid silent hangs.service_tier,prompt_cache_key,prompt_cache_retentiontoOpenAIChatCompletionsRequestso smart-routing stops dropping those too.🤖 Generated with Claude Code